package com.googleplus.ejb;

import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.List;

import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;

import com.googleplus.Person;
import com.googleplus.Post;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.UniformInterfaceException;

/**
 * JSON Implementation of Google+ service.
 */

public class PlusServiceJSONImpl implements PlusService {

	private final String getUrlTemplate = "https://plus.google.com/_/profiles/get/%1$s";
	private final String followingUrlTemplate = "https://plus.google.com/_/socialgraph/lookup/visible/?o=%%5Bnull%%2Cnull%%2C%%22%1$s%%22%%5D";
	private final String follwerUrlTemplate = "https://plus.google.com/_/socialgraph/lookup/incoming/?o=%%5Bnull%%2Cnull%%2C%%22%1$s%%22%%5D&n=1000";
	private final String postUrlTemplate = "https://plus.google.com/_/stream/getactivities/%1$s/?sp=%%5B1%%2C2%%2C%%22%1$s%%22%%2Cnull%%2Cnull%%2Cnull%%2Cnull%%2C%%22social.google.com%%22%%2C%%5B%%5D%%5D&rt=j";
	private final Client restClient;

	/**
	 * Default constructor.
	 */
	public PlusServiceJSONImpl() {
		restClient = new Client();
		restClient.addFilter(new GoogleJSONFilter());
	}

	@Override
	public Person getPerson(final String id) throws Exception {
		JSONArray json;
		try {
			json = getJSON(buildURL(getUrlTemplate,id));
			Person person = parsePersonFromJSON(json);
			return person;
		} catch (Exception e) {
			throw new Exception("Error getting person.", e);
		}
	}


	@Override
	public ArrayList<Person> getFollowers(String id) throws Exception {
		JSONArray json;
		try {
			json = getJSON(buildURL(follwerUrlTemplate,id));
			ArrayList<Person> people = parseListOfPeopleRefs(json);
			return people;
		} catch (Exception e) {
			throw new Exception("Error getting person.", e);
		}
	}

	@Override
	public ArrayList<Person> getFollowing(String id) throws Exception {
		JSONArray json;
		try {
			json = getJSON(buildURL(followingUrlTemplate,id));
			ArrayList<Person> people = parseListOfPeopleRefs(json);
			return people;
		} catch (Exception e) {
			throw new Exception("Error getting person.", e);
		}
	}
	
	@Override
	public ArrayList<Post> getPosts(String id) throws Exception{
		JSONArray json;
		try {
			json = getJSON(buildURL(postUrlTemplate,id));
			ArrayList<Post> posts = parseListOfPosts(json);
			return posts;
		} catch (Exception e) {
			throw new Exception("Error getting person.", e);
		}
	}
	
	private URL buildURL(final String template, final String id) throws MalformedURLException {
		return new URL(String.format(template,id));
	}

	private Person parsePersonFromJSON(JSONArray json) throws JSONException {
		Person p = new Person();
		
		p.id = (String)getFromArray(json, 1,0 );
		p.firstName = (String)getFromArray(json, 1,2,4,1 );
		p.lastName = (String)getFromArray(json, 1,2,4,2 );
		p.pictureUrl = (String)getFromArray(json, 1,2,3 );
		p.profileUrl = (String)getFromArray(json, 1,2,2 );
		p.dateFetched = new Date();
		
		return p;
	}
	
	private Post parsePostFromJSON(JSONArray json) throws JSONException {
		Post p = new Post();
		
		p.id = (String)getFromArray(json, 21 );
		p.author = (String)getFromArray(json,3);//3
		p.subject = (String)getFromArray(json,4);//4
		p.source = (String)getFromArray(json,2);//2
		

		Long timestampNanos = (Long)getFromArray(json,30);
		Long timestampMillis = timestampNanos / 1000000;
		Date postDate = new Date(timestampMillis);
		// this is not quite working yet...
		p.datePosted = null;
		
		p.dateFetched = new Date();
		
		return p;
	}
	
	private ArrayList<Post> parseListOfPosts(JSONArray json) throws JSONException {
		ArrayList<Post> result = new ArrayList<Post>();
		
		JSONArray postList = (JSONArray)getFromArray(json, 0, 0, 1, 0);
		
		for( int i = 0; i < postList.length(); i++ ){
			Post p = parsePostFromJSON(postList.getJSONArray(i));
			result.add(p);
		}
		
		return result;
	}
	
	private ArrayList<Person> parseListOfPeopleRefs(JSONArray json) throws JSONException {
		ArrayList<Person> result = new ArrayList<Person>();
		
		JSONArray personList = (JSONArray)getFromArray(json, 0, 2);
		
		for( int i = 0; i < personList.length(); i++ ){
			
			Person p = new Person();
			p.id = (String)getFromArray(personList.getJSONArray(i), 0, 2);
			result.add(p);
		}
		
		return result;
	}

	private Object getFromArray(JSONArray json, int... i) throws JSONException{
		int idx = i[0];
		Object obj = json.get(idx);
		if( obj instanceof String ){
			return obj;
		}
		if( obj instanceof Long ){
			return obj;
		}
		if( obj instanceof JSONArray ){
			if( i.length == 1 ) return obj;
			int[] nextLevel = Arrays.copyOfRange(i, 1, i.length);
			return getFromArray((JSONArray)obj,nextLevel);
		}
		throw new IllegalStateException("Didn't know how to handle object of type: " + obj.getClass().getName());
	}
	
	private JSONArray getJSON(final URL u) throws UniformInterfaceException, URISyntaxException, InterruptedException {
		JSONArray obj = restClient.resource(u.toURI()).get(JSONArray.class);
		// per Jason S, probably a good idea to sleep a while...
		Thread.sleep(200);
		return obj;
	}


}
